use jnim::*;

use ndkm::Window as ANWin;

use crate::android;
use crate::app::bind_global_if_need;
use crate::win::{del_window, new_window, replace_anwin, with_win, with_win_mut, Key, WinKeyboard, WinRef, WinTouch};
use android::content::res::Configuration as Config;
use android::content::Context;
use android::os::Bundle;
use android::view::SurfaceHolder as _;
use android::view::SurfaceHolderObject as SHolder;

jnim::JnimDefineClass! {
	#CLASS[b"com/mindbox/xloop/XLoopHolder\0"]
	struct XHolder {}
}

#[no_mangle]
extern "system" fn Java_com_mindbox_xloop_XLoopHolder_onCreateNative(
	env: &JEnv,
	_: &XHolder,
	id: &jnim::JString,
	context: &Context,
	_: Option<&Bundle>,
) -> JLong {
	let Some(id) = id.try_into_string(env) else {
		return 0;
	};
	let key = new_window(&id);

	bind_global_if_need(env, context);
	with_win_mut(key, |win| {
		let display_metrics = context.get_resources(env)?.get_display_metrics(env)?;
		let scale_factor = display_metrics.r#density(env)?;
		win.scale_factor = Some(scale_factor);
		Some(())
	});
	with_win(key, |win| {
		win.rep()?.on_create(win.win_ref());
		Some(())
	});

	key.to_bits() as JLong
}
#[no_mangle]
extern "system" fn Java_com_mindbox_xloop_XLoopHolder_onDestroyNative(_env: &JEnv, _: &XHolder, key: JLong) {
	let Some(key) = Key::from_bits(key as _) else {
		return;
	};

	with_win(key, |win| {
		win.rep()?.on_destroy(win.win_ref());
		Some(())
	});

	del_window(key);
}
#[no_mangle]
extern "system" fn Java_com_mindbox_xloop_XLoopHolder_onStartNative(_env: &JEnv, _: &XHolder, key: JLong) {
	let Some(key) = Key::from_bits(key as _) else {
		return;
	};
	with_win(key, |win| {
		win.rep()?.on_start(win.win_ref());
		Some(())
	});
}
#[no_mangle]
extern "system" fn Java_com_mindbox_xloop_XLoopHolder_onStopNative(_env: &JEnv, _: &XHolder, key: JLong) {
	let Some(key) = Key::from_bits(key as _) else {
		return;
	};
	with_win(key, |win| {
		win.rep()?.on_stop(win.win_ref());
		Some(())
	});
}
#[no_mangle]
extern "system" fn Java_com_mindbox_xloop_XLoopHolder_onResumeNative(_env: &JEnv, _: &XHolder, _key: JLong) {
	// let Some(key) = Key::from_bits(key as _) else {
	// 	return;
	// };
	// win::activity_on_resume(env, key);
}
#[no_mangle]
extern "system" fn Java_com_mindbox_xloop_XLoopHolder_onPauseNative(_env: &JEnv, _: &XHolder, _key: JLong) {
	// let Some(key) = Key::from_bits(key as _) else {
	// 	return;
	// };
	// win::activity_on_pause(env, key);
}
#[no_mangle]
extern "system" fn Java_com_mindbox_xloop_XLoopHolder_surfaceCreatedNative(env: &JEnv, _: &XHolder, key: JLong, arg0: &SHolder) {
	log::info!("surfaceCreatedNative");
	let Some(key) = Key::from_bits(key as _) else {
		return;
	};
	if let Some(surface) = arg0.get_surface(env) {
		let anwin = ANWin::from_java(env.as_sys(), surface.as_sys());
		replace_anwin(key, anwin);
	}
	with_win(key, |win| {
		win.rep()?.on_resume(win.win_ref());
		Some(())
	});
}
#[no_mangle]
extern "system" fn Java_com_mindbox_xloop_XLoopHolder_surfaceChangedNative(
	env: &JEnv,
	_: &XHolder,
	key: JLong,
	arg0: &SHolder,
	_arg1: JInt,
	arg2: JInt,
	arg3: JInt,
) {
	log::info!("surfaceChangedNative");
	let Some(key) = Key::from_bits(key as _) else {
		return;
	};
	let old = if let Some(surface) = arg0.get_surface(env) {
		let anwin = ANWin::from_java(env.as_sys(), surface.as_sys());
		replace_anwin(key, anwin)
	} else {
		None
	};
	with_win(key, |win| {
		if old.as_ref() != win.anwin() {
			// log::error!("on_pause on_resume");
			if let Some(old) = &old {
				win.rep()?.on_pause(WinRef(Some(old), win));
			}
			win.rep()?.on_resume(win.win_ref());
		} else {
			// log::error!("on_resize");
			win.rep()?.on_resize(win.win_ref(), (arg2 as _, arg3 as _));
		}
		Some(())
	});
}
#[no_mangle]
extern "system" fn Java_com_mindbox_xloop_XLoopHolder_surfaceDestroyedNative(_env: &JEnv, _: &XHolder, key: JLong, _arg0: &SHolder) {
	log::info!("surfaceDestroyedNative");
	let Some(key) = Key::from_bits(key as _) else {
		return;
	};
	let old = replace_anwin(key, None);
	with_win(key, |win| {
		win.rep()?.on_pause(WinRef(old.as_ref(), win));
		Some(())
	});
}
#[no_mangle]
extern "system" fn Java_com_mindbox_xloop_XLoopHolder_surfaceRedrawNeededNative(_env: &JEnv, _: &XHolder, key: JLong, _arg0: &SHolder) {
	log::info!("surfaceRedrawNeededNative");
	let Some(key) = Key::from_bits(key as _) else {
		return;
	};
	with_win(key, |win| {
		log::info!("surfaceRedrawNeededNative {}", win.frame_nano());
		win.rep()?.on_req_draw(win.win_ref(), win.frame_nano());
		use types::WinRef as _;
		win.win_ref().fresh("surface_redraw_needed");
		Some(())
	});
}
#[no_mangle]
extern "system" fn Java_com_mindbox_xloop_XLoopHolder_onConfigurationChangedNative(_env: &JEnv, _: &XHolder, _key: JLong, _arg0: &Config) {
	// let Some(key) = Key::from_bits(key as _) else {
	// 	return;
	// };
	// win::configuration_changed(env, key, arg0);
}
#[no_mangle]
extern "system" fn Java_com_mindbox_xloop_XLoopHolder_onTouchNative(
	env: &JEnv,
	_: &XHolder,
	key: JLong,
	evt: &android::view::MotionEvent,
) -> JBool {
	let Some(key) = Key::from_bits(key as _) else {
		return JFALSE;
	};
	if Some(true)
		== with_win(key, |win| {
			use android::view::MotionEvent;
			match evt.get_action(env)? {
				MotionEvent::ACTION_DOWN | MotionEvent::ACTION_UP | MotionEvent::ACTION_MOVE | MotionEvent::ACTION_CANCEL => {
					win.rep()?.on_touch(win.win_ref(), WinTouch(0, evt, env)); //TODO
					true
				}
				_ => false,
			}
			.into()
		}) {
		JTRUE
	} else {
		JFALSE
	}
}
#[no_mangle]
extern "system" fn Java_com_mindbox_xloop_XLoopHolder_onKeyNative(
	env: &JEnv,
	_: &XHolder,
	key: JLong,
	evt: &android::view::KeyEvent,
) -> JBool {
	let Some(key) = Key::from_bits(key as _) else {
		return JFALSE;
	};
	if with_win(key, |win| {
		use android::view::KeyEvent;
		match evt.get_action(env)? {
			KeyEvent::ACTION_DOWN | KeyEvent::ACTION_UP => {
				win.rep()?.on_keyboard(win.win_ref(), WinKeyboard(evt, env));
				true
			}
			_ => false,
		}
		.into()
	}) == Some(true)
	{
		JTRUE
	} else {
		JFALSE
	}
}
#[no_mangle]
extern "system" fn Java_com_mindbox_xloop_XLoopHolder_onCommitEndNative(env: &JEnv, _: &XHolder, key: JLong, data: &JString) {
	let Some(key) = Key::from_bits(key as _) else {
		return;
	};
	with_win(key, |win| {
		let s = data.try_into_string(env)?;
		let evt = types::Ime::new(types::Action::End, Some(s.into()));
		win.rep()?.on_ime(win.win_ref(), &evt);
		Some(())
	});
}
#[no_mangle]
extern "system" fn Java_com_mindbox_xloop_XLoopHolder_onCommitBackNative(_env: &JEnv, _: &XHolder, key: JLong) {
	let Some(key) = Key::from_bits(key as _) else {
		return;
	};
	with_win(key, |win| {
		let evt = types::Ime::new(types::Action::Back, None);
		win.rep()?.on_ime(win.win_ref(), &evt);
		Some(())
	});
}
